package com.tomtop.core.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.StringUtils;

public final class ExcelPoi {
	@FunctionalInterface
	public interface ReadDataCell<E> {
		void xlh(Row row, int rownum, String sheetName);
	}
	@FunctionalInterface
	public interface DataCell<E> {
		void xlh(Row row, E e, int index, int sheetid);
	}

	@FunctionalInterface
	public interface DataList<E> {
		Iterable<E> getList(int index, int sheetid);
	}

	private static int maxrow = 8000;

	public static <T> void exPort(File outputfile, String sheetname, int startRow, DataList<T> dataList,
			DataCell<T> dataCell) throws IOException {
		exPort(new FileOutputStream(outputfile), sheetname, startRow, dataList, dataCell);
	}

	public static <T> void exPort(OutputStream outputStream, String sheetname, int startRow, DataList<T> dataList,
			DataCell<T> dataCell) throws IOException {
		ExcelPoi.save(exPort(sheetname, startRow, dataList, dataCell), outputStream);
	}

	public static <T> Workbook exPort(String sheetname, int startRow, DataList<T> dataList, DataCell<T> dataCell)
			throws IOException {
		Workbook workbook = new SXSSFWorkbook(40960);
		int pageid = 0;
		int index = 0;
		while (true) {
			Integer rowid;
			if ((rowid = nextPage(index, startRow, pageid++, sheetname, workbook, dataList, dataCell)) == null) {
				break;
			}
			index += rowid;
		}
		return workbook;
	}

	private static <T> Integer nextPage(int index, int startRow, int pageid, String sheetname, Workbook workbook,
			DataList<T> dataList, DataCell<T> dataCell) {
		Sheet sheet = workbook.createSheet(sheetname + "(" + pageid + ")");
		RowId rowid = new RowId(startRow);
		while (true) {
			Iterable<T> it = dataList.getList(index + rowid.id - startRow, pageid);
			if (it == null)
				return null;
			it.forEach(t -> {
				Row row = sheet.createRow(rowid.id);
				dataCell.xlh(row, t, index - startRow + rowid.id++, pageid);
			});
			if (maxrow <= rowid.id)
				return rowid.id - startRow;
		}
	}

	private static class RowId {
		RowId(int startRow) {
			this.id = startRow;
		}

		int id = 0;
	}

	public static void save(Workbook workbook, OutputStream outputStream) throws IOException {
		workbook.write(outputStream);
		outputStream.flush();
		outputStream.close();
		workbook.close();
	}

	public static <E> void imPort(File inputFile, int startRow, ReadDataCell<E> row) throws IOException {
		FileInputStream input = null;
		try {
			input = new FileInputStream(inputFile);
			imPort(input, inputFile.getName().endsWith(".xls"), startRow, row);
		} finally {
			if (input != null)
				input.close();
		}
	}

	public static <E> void imPort(InputStream input, boolean isXls, int startRow, ReadDataCell<E> row) throws IOException {
		Workbook workbook = null;
		try{
			if (isXls) {
				workbook = new HSSFWorkbook(input);
			} else {
				workbook = new XSSFWorkbook(input);
			}
			imPort(workbook, startRow, row);
		}finally {
			if(workbook!=null)
				workbook.close();
		}
	}

	public static <E> void imPort(Workbook workbook, int startRow, ReadDataCell<E> row) {
		for (Sheet sheet : workbook) {
			readSheet(sheet, startRow, row);
		}
	}

	private static <E> void readSheet(Sheet sheet, int startRow, ReadDataCell<E> row) {
		String sheetName = sheet.getSheetName();
		if(startRow<sheet.getFirstRowNum())
			startRow=sheet.getFirstRowNum();
		for ( int rownum=startRow; rownum <= sheet.getLastRowNum(); rownum++) {
			row.xlh(sheet.getRow(rownum), rownum, sheetName);
		}
	}
	public static Map<String,Object> readRow(Row row,int startCell,String[] cellNames){
		Map<String,Object> d=new HashMap<>();
		for (int i = 0; i < cellNames.length; i++) {
			Cell cell=row.getCell(i+startCell) ;
			Object val = ExcelPoi.readCell(cell);
			for (String name : cellNames[i].split("/")) {
				if(StringUtils.hasText(name))
					d.put(name, val);
			}
		}
		return d;
	}
	private static List<String> df=ListUtils.<String>n().a("m/d/yy,h:mm:ss".split(",")).to();
	@SuppressWarnings("deprecation")
	public static Object readCell(Cell cell){
		CellType type ;
		if(cell==null||(type = cell.getCellTypeEnum()) ==null)
			return null;
		switch (type) {
		case NUMERIC:
			if(df.contains(cell.getCellStyle().getDataFormatString())){
				return cell.getDateCellValue();
			}
			else{
				return cell.getNumericCellValue();
			}
		case STRING:
			return cell.getStringCellValue();
		case FORMULA:
			return cell.getNumericCellValue();
		case BLANK:
			return null;
		case BOOLEAN:
			return cell.getBooleanCellValue();
		case ERROR:
			return cell.getErrorCellValue();
		case _NONE:
			break;

		default:
			break;
		}
		return null;
	}
}
